﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using Microsoft.Expression.Controls;
using Microsoft.Expression.Media;
using StrategyMap.UserControls;
using System.Windows.Input;

namespace StrategyMap
{
    public static class Connections
    {
        private static List<TrackConnection> TrackConnections { get; set; }
        private static UserControl FromElement { get; set; }
        private static UserControl ToElement { get; set; }

        static Connections()
        {
            TrackConnections = new List<TrackConnection>();
        }

        public static void Adjust(UserControl connectFromElement, UserControl connectToElement)
        {
            Add(connectFromElement, connectToElement, adjustOnly: true);

            foreach (var connect in TrackConnections.Where(connect => connectFromElement == connect.ToElement))
            {
                Add(connect.FromElement, connect.ToElement, adjustOnly: true);
            }
        }

        //handle objects that are only connected to (they own no MyConnections to other objects)
        public static void Adjust(UserControl connectToElement)
        {
            foreach (var connect in TrackConnections.Where(connect => connectToElement == connect.ToElement))
            {
                Add(connect.FromElement, connect.ToElement, adjustOnly: true);
            }
        }

        public static LineArrow Add(UserControl connectFromElement, UserControl connectToElement, SolidColorBrush color = null, int strokeThickness = 3, double bendAmount = 0, double arrowSize = 5, DoubleCollection strokeDashArray = null, string arrowType = null, bool shadow = false, bool adjustOnly = false)
        {
            if (arrowSize == 0)
                return null;//stupid
            var parent = connectFromElement.Parent as Panel;

            FromElement = connectFromElement;
            ToElement = connectToElement;

            var connectFromX = connectFromElement.Left();
            var connectFromY = connectFromElement.Top();
            var connectToX = connectToElement.Left();
            var connectToY = connectToElement.Top();

            switch (PositionKey(connectFromElement, connectToX - connectFromX, connectToY - connectFromY))
            {
                case "RA": //right & above
                case "LA": //left & above
                case "SA": //same horizontal & above
                    AboveOrBelow(connectToElement, connectFromElement, ref connectFromX, ref connectFromY, ref connectToX);
                    break;

                case "LB": //left & below
                case "RB": //right & below
                    AboveOrBelow(connectFromElement, connectToElement, ref connectToX, ref connectToY, ref connectFromX);
                    break;

                case "SB": //same vertical & below
                    AboveOrBelow(connectToElement, connectFromElement, ref connectFromX, ref connectFromY, ref connectToX);
                    break;

                case "RS": //right & same vertical
                    RightOrLeft(connectFromElement, connectToElement, ref connectToY, ref connectToX, ref connectFromY);
                    break;

                case "LS": //left & same vertical
                    RightOrLeft(connectToElement, connectFromElement, ref connectFromY, ref connectFromX, ref connectToY);
                    break;

                case "SS":
                    //do nothing if same horizontal and vertical
                    break;
            }

            Canvas.SetZIndex(connectFromElement, 1);
            Canvas.SetZIndex(connectToElement, 1);

            if (adjustOnly == false)
            {
                return AddConnection(parent, connectFromX, connectFromY, connectToX, connectToY, color, strokeThickness, bendAmount, arrowSize, strokeDashArray, arrowType, shadow);
            }
            else
            {
                //AdjustConnection(parent, connectFromX -320 -100, connectFromY -166, connectToX-320-100, connectToY-100);
                AdjustConnection(parent, connectFromX, connectFromY, connectToX, connectToY);
                return null;
            }
        }

        private static LineArrow AddConnection(Panel parent, double x1, double y1, double x2, double y2, SolidColorBrush color, int strokeThickness, double bendAmount, double arrowSize, DoubleCollection strokeDashArray, string arrowType, bool shadow)
        {
            if (color == null)
            {
                color = new SolidColorBrush(Colors.Black);
            }

            double height = Math.Abs(y2 - y1);
            double width = Math.Abs(x2 - x1);
            double adjY = 0;
            double adjX = 0;

            var cornerType = CalcCornerType(x1, y1, x2, y2, width, height, ref adjX, ref adjY);

            var line = new LineArrow
            {
                //Name = Guid.NewGuid().ToString(),
                StartCorner = cornerType,
                Stroke = color,
                BendAmount = bendAmount,
                ArrowSize = arrowSize,
                StrokeDashArray = strokeDashArray,
                EndArrow = arrowType.ArrowTypeFromString(),
                Height = height,
                Width = width,
                StrokeThickness = strokeThickness
            };

            //line.MouseEnter += new System.Windows.Input.MouseEventHandler(line_MouseEnter);
            //line.MouseLeave += new MouseEventHandler(line_MouseLeave);
            //line.MouseLeftButtonDown += new MouseButtonEventHandler(line_MouseLeftButtonDown);

            if (shadow == true)
            {
                line.Effect = new DropShadowEffect { Color = Colors.Gray, BlurRadius = 5 };
            }

            parent.Children.Add(line);

            line.SetValue(Canvas.LeftProperty, x1 + adjX);
            line.SetValue(Canvas.TopProperty, y1 + adjY);

            TrackConnections.Add(new TrackConnection { FromElement = FromElement, ToElement = ToElement, Line = line });

            Canvas.SetZIndex(line, 0);

            return line;
        }

        /*static void line_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //((LineArrow)sender).Cursor = Cursors.Hand;

        }

        static void line_MouseLeave(object sender, MouseEventArgs e)
        {
            //((LineArrow)sender).Cursor = Cursors.Arrow;

            ((LineArrow)sender).Stroke = new SolidColorBrush(Colors.Black);

        }

        static void line_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            //((LineArrow)sender).Cursor = Cursors.Eraser;
            //System.Threading.Thread.Sleep(2000);
          //  ((LineArrow)sender).Cursor = Cursors.Hand;

            ((LineArrow)sender).Stroke = new SolidColorBrush(Colors.Yellow);
          
        }*/

        private static void AdjustConnection(Panel parent, double x1, double y1, double x2, double y2)
        {

            foreach (var connectionObject in TrackConnections)
            {
                if (connectionObject.FromElement == FromElement && connectionObject.ToElement == ToElement)
                {
                    double height = Math.Abs((y2 - parent.Top()) - (y1 - parent.Top()));
                    double width = Math.Abs((x2 - parent.Left()) - (x1 - parent.Left()));
                    double adjY = 0;
                    double adjX = 0;

                    var cornerType = CalcCornerType(x1, y1, x2, y2, width, height, ref adjX, ref adjY);

                    connectionObject.Line.SetValue(Canvas.TopProperty, (y1 - parent.Top()) + adjY);
                    connectionObject.Line.SetValue(Canvas.LeftProperty, (x1 - parent.Left()) + adjX);
                    connectionObject.Line.StartCorner = cornerType;
                    connectionObject.Line.Height = Math.Abs(height);
                    connectionObject.Line.Width = Math.Abs(width);
                }
            }
        }

        private static string PositionKey(UserControl connectFromElement, double diffX, double diffY)
        {
            string verticalPosition;
            string horizontalPosition;

            if (Math.Abs(diffY) < ((Node1)connectFromElement).nHeight)
            {
                diffY = 0;
            }

            if (diffX == 0)
            {
                horizontalPosition = "S"; //same horizontal
            }
            else
            {
                if (diffX > 0)
                {
                    horizontalPosition = "L"; //left
                }
                else
                {
                    horizontalPosition = "R"; //right
                }
            }

            if (diffY == 0)
            {
                verticalPosition = "S"; //same vertical
            }
            else
            {
                if (diffY > 0)
                {
                    verticalPosition = "A"; //above
                }
                else
                {
                    verticalPosition = "B"; //below
                }
            }

            return horizontalPosition + verticalPosition;
        }

        private static void AboveOrBelow(UserControl element1, UserControl element2, ref double p1, ref double p2, ref double p3)
        {
            //p1 += 163 / 2;
            //p2 += 81 / 2;
            //p3 += 163 / 2;
            p1 += ((Node1)element2).nWidth / 2;
            p2 += ((Node1)element2).nHeight;
            p3 += ((Node1)element1).nWidth / 2;
        }

        private static void RightOrLeft(UserControl element1, UserControl element2, ref double p1, ref double p2, ref double p3)
        {
            p1 += ((Node1)element2).nHeight / 2;
            p2 += ((Node1)element2).nWidth;
            p3 += ((Node1)element1).nHeight / 2;
        }

        private static CornerType CalcCornerType(double x1, double y1, double x2, double y2, double width, double height, ref double adjX, ref double adjY)
        {
            var cornerType = CornerType.TopLeft;

            if (y2 < y1 && x2 < x1)
            {
                cornerType = CornerType.BottomRight;
                adjY = -height;
                adjX = -width;
            }
            else
            {
                if (y2 < y1)
                {
                    cornerType = CornerType.BottomLeft;
                    adjY = -height;
                }

                if (x2 < x1)
                {
                    cornerType = CornerType.TopRight;
                    adjX = -width;
                }
            }
            return cornerType;
        }

        public static double Left(this Object value)
        {
            if (value.GetType() == typeof(Canvas))
            {
                //return -100;
                var g = ((UIElement)value).TransformToVisual(Application.Current.RootVisual);
                var offset = g.Transform(new Point(0, 0));
                return offset.X;
            }
            else
            {
                var g = ((UserControl)value).TransformToVisual(Application.Current.RootVisual);
                var offset = g.Transform(new Point(0, 0));
                return offset.X;
            }
        }

        public static double Top(this Object value)
        {
            if (value.GetType() == typeof(Canvas))
            {
                var g = ((UIElement)value).TransformToVisual(Application.Current.RootVisual);
                var offset = g.Transform(new Point(0, 0));
                return offset.Y;
            }
            else
            {
                var g = ((UserControl)value).TransformToVisual(Application.Current.RootVisual);
                var offset = g.Transform(new Point(0, 0));
                return offset.Y;
            }
        }

        public static ArrowType ArrowTypeFromString(this string value)
        {
            switch (value)
            {
                case "Arrow":
                    return ArrowType.Arrow;

                case "NoArrow":
                    return ArrowType.NoArrow;

                case "OpenArrow":
                    return ArrowType.OpenArrow;

                case "OvalArrow":
                    return ArrowType.OvalArrow;

                case "StealthArrow":
                    return ArrowType.StealthArrow;

                default://change this value for a default arrow type
                    return ArrowType.StealthArrow;
            }
        }
    }
}
